/*-------------------------------------------------------*/
/* Easy_Timer: [c]copyright 1995 by AlphaHelix           */
/*                                                       */
/* This is some kind of a strange timer module.          */
/*-------------------------------------------------------*/


#include <dos.h>
#include <conio.h>

#include "types.h"

#include "timer.hpp"


// Private data.
//--------------

// Pointer to old timer interrupt.
static void (__interrupt __far *int08)(void);

// Registered function.
#define MAXREGS      3        // Max possible registered functions.
static int     nregs = 0;     // Number of registered exit functions.
static void    (*reg_func[MAXREGS])(void);

// Some stuff
static int   et_mode;          // SOme flags.
static int   vspeed;           // Current timer speed.


static void starttimer1(int speed)
{
   outp(0x43, 0x36);
   outp(0x40, speed&0xff);
   outp(0x40, speed>>8);
}

// This is the new interrupt handler.
static void __interrupt newint08(void)
{
   static   old08 = 0;
   int      i;

   _enable();

// Call old 08 handler about 18 times/sec.
   old08 += vspeed;
   if (old08 > 0x10000) {
      old08 -= 0x10000;
      if (et_mode & ET_CALLOLD) {
         int08();
      } else {
         outp(0x20, 0x20);
      }
   } else {
      outp(0x20, 0x20);
   }

// At last execute registered functions.
   for (i = 0; i < nregs; i++) reg_func[i]();
}


// Register a new function.
// This function will be called on every timer interrupt.
/* WATCH OUT HERE:
   Because the flat memory model assumes DS==SS by default
   you MUSTN'T register a function defining a local variable AND
   using its address in any way.
   On a interrupt DS is usually !=SS ---> but the registered
   function assumes it is. ----> crash.
   Example.

   void some_func(void)
   {
      int   a;
      int   b[20];

      a = 1;
      ...
      call_bad_func(&a);  <------ THIS is no good. You are using addr of a.
      call_good_func(a);  <------ Alright. Just value passed.
      ...
      b[a] = 0; <-- This is also very dangerous. The compiler might try to
                    access the array in DS.
   }
   This is true for ALL functions in the call tree. So be especially
   careful in calling library functions you don't know what they are doing.
   Best solution to this problem is to define all local variables as static.
*/
void ET_reg(void (*f)(void))
{
   if (nregs < MAXREGS) reg_func[nregs++] = f;
}

void ET_setmode(int mode)
{
   et_mode = mode;
}

// Initialize timer function.
// Hook interrupts, lock DPMI memory.
void ET_init(int hz)
{
// First clear variables.
   nregs = 0;
   ET_setmode(0);

// Capture user timer interrupt 0x1c.
   int08 = _dos_getvect(0x08);
   _dos_setvect(0x08, newint08);

// Calculate timer speed.
   vspeed = 1193181L / hz;
// Start timer.
   starttimer1(vspeed);

}

void ET_shut(void)
{
   starttimer1(0);      // Reset timer speed to 18.2 ticks per sec.
   _dos_setvect(0x08, int08);
}


